using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Collections;
using System.Windows.Forms;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Resources;
using ObjectLib;
using EngMATLib;
using FftLib;
using BmgNet;
using StaticUtility;
using System.Collections.Generic;

namespace ObjectParser
{
    /// <summary>
    /// A command capable of being sequenced into the application's Undo/Redo stack of events.  Each action
    /// can be executed (via Redo) or reversed (via Undo).
    /// </summary>
    public abstract class Command
    {
        public abstract void Redo();
        public abstract void Undo();
    }
    /// <summary>
    /// Command to claim an edge.
    /// Replaces Undo/Redo call sign 120.
    /// </summary>
    public class Cmd_ClaimEdge : Command
    {
        public EdgeGroup Group;
        public PolygonF OldOwner;
        public PolygonF NewOwner;
        public Cmd_ClaimEdge() {}
        public Cmd_ClaimEdge(EdgeGroup Group, PolygonF OldOwner, PolygonF NewOwner)
        {
            this.Group = Group;
            this.OldOwner = OldOwner;
            this.NewOwner = NewOwner;
        }
        public override void Redo()
        {
            Redo(OldOwner, NewOwner);
            ((FormParse)Group[0].Owner.Parser).Pb.Invalidate();
        }
        public override void Undo()
        {
            Redo(NewOwner, OldOwner);
            ((FormParse)Group[0].Owner.Parser).Pb.Invalidate();
        }
        public void Redo(PolygonF oldOwner, PolygonF newOwner)
        {
            int i;
            //	For the old owner, the Edge must be removed from the list of OwnedEdges
            //	remove from old owner (if not null)
            if(oldOwner != null)
            {
                for(i=0;i<Group.Count;i++)
                    oldOwner.OwnedEdges.Remove(Group[i]);
                Group.DominantInd=-1;
            }

            //	For the new owner, the Edge must be added to the list of OwnedEdges
            //	remove from old owner (if not null)
            if(newOwner != null)
            {
                for(i=0;i<Group.Count;i++)
                    if(Group[i].Owner==newOwner)
                    {
                        Group.DominantInd=i;
                        if( !newOwner.OwnedEdges.Contains(Group[i]) )
                            newOwner.OwnedEdges.Add(Group[i]);
                    }
            }
            
            //	For the old owner, the Edge must be removed from the list of OwnedEdges
            //	remove from old owner (if not null)
            if(oldOwner != null)
            {
                for(i=0;i<Group.Count;i++)
                    oldOwner.OwnedEdges.Remove(Group[i]);
                Group.DominantInd=-1;
            }

            //	For the new owner, the Edge must be added to the list of OwnedEdges
            //	remove from old owner (if not null)
            if(newOwner != null)
            {
                for(i=0;i<Group.Count;i++)
                    if(Group[i].Owner==newOwner)
                    {
                        Group.DominantInd=i;
                        if(!newOwner.OwnedEdges.Contains(Group[i]))
                            newOwner.OwnedEdges.Add(Group[i]);
                    }
            }
        }
    }
    /// <summary>
    /// Command to release an edge.
    /// Replaces Undo/Redo call sign 121.
    /// </summary>
    public class Cmd_ReleaseEdge : Command
    {
        /// <summary>
        /// This reduces to a Cmd_ClaimEdge where NewOwner = null;
        /// </summary>
        public Cmd_ClaimEdge Com;
        public Cmd_ReleaseEdge() {}
        public Cmd_ReleaseEdge(EdgeGroup Group, PolygonF oldOwner)
        {
            Com = new Cmd_ClaimEdge(Group, oldOwner, null);
        }
        public override void Redo()
        {
            Com.Redo();
        }
        public override void Undo()
        {
            Com.Undo();
        }
    }
    /// <summary>
    /// Command to claim all edges of a given polygon assuming it's "CalcEdges" function has already been called.
    /// Replaces Undo/Redo call sign 122.
    /// </summary>
    public class Cmd_ClaimAllEdges : Command
    {
        public PolygonF NewOwner;
        public Cmd_ClaimEdge[] Cmd;
        public Cmd_ClaimAllEdges() {}
        public Cmd_ClaimAllEdges(PolygonF NewOwner)
        {
            this.NewOwner = NewOwner;
            this.Cmd = new Cmd_ClaimEdge[NewOwner.EdgeGroups.Count];
            EdgeGroup eg;
            PolygonF oldOwner;
            for(int i=0; i<NewOwner.EdgeGroups.Count; i++)
            {
                eg = NewOwner.EdgeGroups[i];
                oldOwner = null;
                if( eg.DominantInd > -1 )
                    oldOwner = eg.DominantEdge.Owner;
                if( oldOwner != NewOwner )
                    Cmd[i] = new Cmd_ClaimEdge(eg, oldOwner, NewOwner);
            }
        }
        public override void Undo()
        {
            for(int i=0; i<Cmd.Length; i++)
                if(Cmd[i]!=null)
                    Cmd[i].Undo();
        }
        public override void Redo()
        {
            for(int i=0;i<Cmd.Length;i++)
                if(Cmd[i]!=null)
                    Cmd[i].Redo();
        }
    }
    /// <summary>
    /// Command to claim all of the unclaimed edges of a given polygon assuming it's "CalcEdges" function has already been called.
    /// Replaces Undo/Redo call sign 123.
    /// </summary>
    public class Cmd_ClaimUnclaimedEdges : Command
    {
        PolygonF NewOwner;
        public Cmd_ClaimEdge[] Cmd;
        public Cmd_ClaimUnclaimedEdges() {}
        public Cmd_ClaimUnclaimedEdges(PolygonF NewOwner)
        {
            this.NewOwner = NewOwner;

            this.NewOwner = NewOwner;
            this.Cmd = new Cmd_ClaimEdge[NewOwner.EdgeGroups.Count];
            EdgeGroup eg;
            for(int i=0;i<NewOwner.EdgeGroups.Count;i++)
            {
                eg = NewOwner.EdgeGroups[i];
                if(eg.DominantInd == -1)
                    Cmd[i] = new Cmd_ClaimEdge(eg,null,NewOwner);
            }
        }
        public override void Undo()
        {
            for(int i=0;i<Cmd.Length;i++)
                if(Cmd[i]!=null)
                    Cmd[i].Undo();
        }
        public override void Redo()
        {
            for(int i=0;i<Cmd.Length;i++)
                if(Cmd[i]!=null)
                    Cmd[i].Redo();
        }
    }
    /// <summary>
    /// Deletes a VertexF from all polygons and contours, may switch active thing after the operation.
    /// Replaces Undo/Redo call sign 102.
    /// </summary>
    public class Cmd_DeleteVertex : Command
    {
        /// <summary>
        /// The deleted vertex.
        /// </summary>
        public VertexF Vertex;
        /// <summary>
        /// The active polygon.
        /// </summary>
        public PolygonF ActPoly;
        /// <summary>
        /// The next active thing after the deletion of the vertex.
        /// </summary>
        public object NextAct;
        /// <summary>
        /// The previous contents of the deleted vertex member "Polygons"
        /// </summary>
        public PolyFarray Polygons;
        /// <summary>
        /// The previous contents of the deleted vertex member "Contours"
        /// </summary>
        public ContourFarray Contours;
        /// <summary>
        /// The indices where the vertex goes within each member of "Polygons"
        /// </summary>
        public int[] PolyIndices;
        /// <summary>
        /// The indices where the vertex goes within each member of "Contours"
        /// </summary>
        public int[] ConIndices;
        /// <summary>
        /// List of polygons to swallow and unswallow during the execution of this operation.
        /// </summary>
        public PolyFarray ToSwallow, ToUnswallow;
        /// <summary>
        /// List of edge groups invalidated by this operation.
        /// </summary>
        public EdgeGroupArray Ega;
        /// <summary>
        /// List of occlusion bridges invalidated by this operation.
        /// </summary>
        public OcclusionBridgeList Obl;
        /// <summary>
        /// Empty constructor.
        /// </summary>
        public Cmd_DeleteVertex() {}
        /// <summary>
        /// Full constructor.
        /// </summary>
        /// <param name="Vertex"></param>
        /// <param name="ActPoly"></param>
        /// <param name="NextAct"></param>
        /// <param name="ToSwallow"></param>
        /// <param name="ToUnswallow"></param>
        /// <param name="Polygons">[can be null for "Redo" call, auto-updating]</param>
        /// <param name="Contours">[can be null for "Redo" call, auto-updating]</param>
        /// <param name="PolyIndices">[can be null for "Redo" call, auto-updating]</param>
        /// <param name="ConIndices">[can be null for "Redo" call, auto-updating]</param>
        /// <param name="Ega">[can be null for "Redo" call, auto-updating]</param>
        /// <param name="Obl">[can be null for "Redo" call, auto-updating]</param>
        public Cmd_DeleteVertex(VertexF Vertex, PolygonF ActPoly, object NextAct, PolyFarray ToSwallow, PolyFarray ToUnswallow, 
            PolyFarray Polygons, ContourFarray Contours, int[] PolyIndices, int[] ConIndices, EdgeGroupArray Ega, OcclusionBridgeList Obl)
        {
            this.Vertex = Vertex;
            this.Polygons = Polygons;
            this.Contours = Contours;
            this.PolyIndices = PolyIndices;
            this.ConIndices = ConIndices;
            this.ToSwallow = ToSwallow;
            this.ToUnswallow = ToUnswallow;
            this.Ega = Ega;
            this.Obl = Obl;
            this.ActPoly = ActPoly;
            this.NextAct = NextAct;
        }
        public override void Redo()
        {
            //	Swallowing requires doubly linking two lists.
            if(ToSwallow != null)
            {
                for(int i=0;i<ToSwallow.Count;i++)
                {
                    ActPoly.Swallowed.Add(ToSwallow[i]);
                    ToSwallow[i].SwallowedBy = ActPoly;
                }
            }
            if(ToUnswallow != null)
            {
                for(int i=0;i<ToUnswallow.Count;i++)
                {
                    ActPoly.Swallowed.Remove(ToUnswallow[i]);
                    ToUnswallow[i].SwallowedBy = null;
                }
            }

            //  Perform the actual deletion
            Vertex.RemoveFromAllOwners(out PolyIndices,out ConIndices);
            Vertex.RemoveAllOwners(out Polygons, out Contours);

            //  Do this after all the vertices have been handled in the for loop preceding
            for(int i=0;i<Polygons.Count;i++)
                Polygons[i].AfterRemoveVertexF(PolyIndices[i],Vertex,
                    ((FormParse)ActPoly.Parser).Ptool.GetBitmap(Polygons[i]),
                    ((FormParse)ActPoly.Parser).Ptool.GetColor(Polygons[i]));

            //	Keep track of busted edges after removing VertexF
            Ega = ActPoly.ValidateEdges();

            //	Keep track of busted bridges after removing VertexF
            Obl = ActPoly.Owner.ValidateBridges();

            //  Switch to the next active object
            ((FormParse)ActPoly.Parser).ActivateSelection(NextAct,false);
			((FormParse)ActPoly.Parser).Ptool.UpdateVertexDraw();
            ((FormParse)ActPoly.Parser).Ptool.UpdateInactivePaths();
        }
        public override void Undo()
        {
            int i;
            //	Swallowing requires doubly linking two lists.
            if(ToSwallow != null)
            {
                for(i=0;i<ToSwallow.Count;i++)
                {
                    ActPoly.Swallowed.Remove(ToSwallow[i]);
                    ToSwallow[i].SwallowedBy = null;
                }
            }
            if(ToUnswallow != null)
            {
                for(i=0;i<ToUnswallow.Count;i++)
                {
                    ActPoly.Swallowed.Add(ToUnswallow[i]);
                    ToUnswallow[i].SwallowedBy = ActPoly;
                }
            }

            //  Perform the actual restoration
            Vertex.Polygons = Polygons;
            Vertex.Contours = Contours;
            for(i=0; i<Polygons.Count; i++)
                Polygons[i].Insert(PolyIndices[i], Vertex);
            for(i=0;i<Contours.Count;i++)
                Contours[i].Insert(ConIndices[i], Vertex);

            //  Do this after all the vertices have been handled in the for loop preceding
            for(i=0;i<Polygons.Count;i++)
                Polygons[i].AfterAddVertexF(PolyIndices[i],Vertex,
                    ((FormParse)ActPoly.Parser).Ptool.GetBitmap(Polygons[i]),
                    ((FormParse)ActPoly.Parser).Ptool.GetColor(Polygons[i]));

            //  Switch to the next active object
            ((FormParse)ActPoly.Parser).ActivateSelection(ActPoly,false);
            ((FormParse)ActPoly.Parser).Ptool.UpdateVertexDraw();
			((FormParse)ActPoly.Parser).Ptool.UpdateInactivePaths();

            //	Restore busted bridges only on Undo
            ActPoly.Owner.Category.Owner.RestoreBridges(Obl);

            //	Restore busted edges only on Undo (and recalculate edges).
            ActPoly.Owner.Category.Owner.RestoreEdges(Ega,true);
        }
    }
    /// <summary>
    /// Adds a VertexF to a set of polygons and contours, may switch active thing after operation.
    /// Replaces Undo/Redo call sign 101.
    /// </summary>
    public class Cmd_AddVertex : Command
    {
        /// <summary>
        /// The PolygonF that swallows this active poly.
        /// </summary>
        public PolygonF ToBeSwallowedBy;
        /// <summary>
        /// The PolygonF that unswallows this active poly to make way for ToBeSwallowedBy.
        /// </summary>
        public PolygonF ToBeUnswallowedBy;
        /// <summary>
        /// This command is the reverse of a delete command, therefore, the functionality of this class
        /// is based upon its DeleteCmd object.
        /// </summary>
        public Cmd_DeleteVertex DeleteCmd = null;
        /// <summary>
        /// Empty constructor
        /// </summary>
        public Cmd_AddVertex() {}
        /// <summary>
        /// Full constructor.
        /// </summary>
        /// <param name="Vertex"></param>
        /// <param name="ActPoly"></param>
        /// <param name="NextAct"></param>
        /// <param name="ToSwallow"></param>
        /// <param name="ToUnswallow"></param>
        /// <param name="Polygons">[can be null for "Undo" call, auto-updating]</param>
        /// <param name="Contours">[can be null for "Undo" call, auto-updating]</param>
        /// <param name="PolyIndices">[can be null for "Undo" call, auto-updating]</param>
        /// <param name="ConIndices">[can be null for "Undo" call, auto-updating]</param>
        /// <param name="Ega">[can be null for "Undo" call, auto-updating]</param>
        /// <param name="Obl">[can be null for "Undo" call, auto-updating]</param>
        public Cmd_AddVertex(VertexF Vertex,PolygonF ActPoly,PolygonF NextAct,PolyFarray ToSwallow,PolyFarray ToUnswallow,PolygonF ToBeSwallowedBy, PolygonF ToBeUnswallowedBy,
            PolyFarray Polygons, ContourFarray Contours, int[] PolyIndices, int[] ConIndices, EdgeGroupArray Ega, OcclusionBridgeList Obl)
        {
            this.ToBeSwallowedBy = ToBeSwallowedBy;
            this.ToBeUnswallowedBy = ToBeUnswallowedBy;
            DeleteCmd = new Cmd_DeleteVertex(Vertex,ActPoly,NextAct,ToSwallow,ToUnswallow,Polygons,Contours,PolyIndices,ConIndices,Ega,Obl);
        }
        public override void Redo()
        {
            DeleteCmd.Undo();
            if(ToBeUnswallowedBy!=null)
            {
                ToBeUnswallowedBy.Swallowed.Remove(DeleteCmd.ActPoly);
                DeleteCmd.ActPoly.SwallowedBy = null;
            }
            if(ToBeSwallowedBy!=null)
            {
                ToBeSwallowedBy.Swallowed.Add(DeleteCmd.ActPoly);
                DeleteCmd.ActPoly.SwallowedBy = ToBeSwallowedBy;
            }
        }
        public override void Undo()
        {
            DeleteCmd.Redo();
            if(ToBeSwallowedBy!=null)
            {
                ToBeSwallowedBy.Swallowed.Remove(DeleteCmd.ActPoly);
                DeleteCmd.ActPoly.SwallowedBy = null;
            }
            if(ToBeUnswallowedBy!=null)
            {
                ToBeUnswallowedBy.Swallowed.Add(DeleteCmd.ActPoly);
                DeleteCmd.ActPoly.SwallowedBy = ToBeUnswallowedBy;
            }
        }
    }
    /// <summary>
    /// Move a VertexF, replaces Undo/Redo call sign 104.
    /// </summary>
    public class Cmd_MoveVertex : Command
    {
        VertexF Vertex;
        PointF OldLoc;
        PointF NewLoc;
        /// <summary>
        /// List of polygons to swallow and unswallow during the execution of this operation.
        /// </summary>
        public PolyFarray ToSwallow,ToUnswallow;
        /// <summary>
        /// The active polygon at the time.
        /// </summary>
        public PolygonF ActPoly;
        public Cmd_MoveVertex() {}
        public Cmd_MoveVertex(VertexF Vertex, PointF OldLoc, PointF NewLoc, PolygonF ActPoly, PolyFarray ToSwallow, PolyFarray ToUnswallow)
        {
            this.Vertex = Vertex;
            this.OldLoc = OldLoc;
            this.NewLoc = NewLoc;
            this.ToSwallow = ToSwallow;
            this.ToUnswallow = ToUnswallow;
            this.ActPoly = ActPoly;
        }
        public override void Redo()
        {
            if(ToSwallow != null)
            {
                for(int i=0;i<ToSwallow.Count;i++)
                {
                    ActPoly.Swallowed.Add(ToSwallow[i]);
                    ToSwallow[i].SwallowedBy = ActPoly;
                }
            }
            if(ToUnswallow != null)
            {
                for(int i=0;i<ToUnswallow.Count;i++)
                {
                    ActPoly.Swallowed.Remove(ToUnswallow[i]);
                    ToUnswallow[i].SwallowedBy = null;
                }
            }

            //  I don't remember what this does, but I'm copying in from another section because I don't want to make a new bug.
            bool oldMovingPathPt = ((FormParse)ActPoly.Parser).Ptool.MovingPathPt;
            PathF pf;
            for(int i=0;i<Vertex.Polygons.Count;i++)
            {
                pf = Vertex.Polygons[i];
                Vertex.X = NewLoc.X;    //  Redundant, should only be done once, but doesn't hurt to do it every time, makes simpler code.
                Vertex.Y = NewLoc.Y;    //  Redundant, should only be done once, but doesn't hurt to do it every time, makes simpler code.
                int ind = pf.IndexOf(Vertex);
                //pf.PbPath[ind].X = ((FormParse)ActPoly.Parser).ImgToPbX(Vertex.X);
                //pf.PbPath[ind].Y = ((FormParse)ActPoly.Parser).ImgToPbY(Vertex.Y);
                ((PolygonF)pf).AfterMoveVertexF(
                    ind, OldLoc,
                    ((FormParse)ActPoly.Parser).Ptool.GetBitmap(((PolygonF)pf)),
                    ((FormParse)ActPoly.Parser).Ptool.GetColor(((PolygonF)pf)));
            }
            for(int i=0;i<Vertex.Contours.Count;i++)
            {
                pf = Vertex.Contours[i];
                Vertex.X = NewLoc.X;    //  Redundant, should only be done once, but doesn't hurt to do it every time, makes simpler code.
                Vertex.Y = NewLoc.Y;    //  Redundant, should only be done once, but doesn't hurt to do it every time, makes simpler code.
                int ind = pf.IndexOf(Vertex);
				pf.PbPath[ind] = ((FormParse)ActPoly.Parser).ImgToPb(Vertex.PointF);
            }
            ((FormParse)ActPoly.Parser).Ptool.UpdateVertexDraw();
			((FormParse)ActPoly.Parser).Ptool.UpdateInactivePaths();

            ((FormParse)ActPoly.Parser).Ptool.MovingPathPt = oldMovingPathPt;   //  complement to the expression above, "bool oldMovingPathPt = ..."
        }
        public override void Undo()
        {
            if(ToUnswallow != null)
            {
                for(int i=0;i<ToUnswallow.Count;i++)
                {
                    ActPoly.Swallowed.Add(ToUnswallow[i]);
                    ToUnswallow[i].SwallowedBy = ActPoly;
                }
            }
            if(ToSwallow != null)
            {
                for(int i=0;i<ToSwallow.Count;i++)
                {
                    ActPoly.Swallowed.Remove(ToSwallow[i]);
                    ToSwallow[i].SwallowedBy = null;
                }
            }

            //  I don't remember what this does, but I'm copying in from another section because I don't want to make a new bug.
            bool oldMovingPathPt = ((FormParse)ActPoly.Parser).Ptool.MovingPathPt;
            PathF pf;
            for(int i=0;i<Vertex.Polygons.Count;i++)
            {
                pf = Vertex.Polygons[i];
                Vertex.X = OldLoc.X;    //  Redundant, should only be done once, but doesn't hurt to do it every time, makes simpler code.
                Vertex.Y = OldLoc.Y;    //  Redundant, should only be done once, but doesn't hurt to do it every time, makes simpler code.
                int ind = pf.IndexOf(Vertex);
				pf.PbPath[ind] = ((FormParse)ActPoly.Parser).ImgToPb(Vertex.PointF);
                ((PolygonF)pf).AfterMoveVertexF(ind,NewLoc,
                    ((FormParse)ActPoly.Parser).Ptool.GetBitmap(((PolygonF)pf)),
                    ((FormParse)ActPoly.Parser).Ptool.GetColor(((PolygonF)pf)));
            }
            for(int i=0;i<Vertex.Contours.Count;i++)
            {
                pf = Vertex.Contours[i];
                Vertex.X = OldLoc.X;    //  Redundant, should only be done once, but doesn't hurt to do it every time, makes simpler code.
                Vertex.Y = OldLoc.Y;    //  Redundant, should only be done once, but doesn't hurt to do it every time, makes simpler code.
                int ind = pf.IndexOf(Vertex);
				pf.PbPath[ind] = ((FormParse)ActPoly.Parser).ImgToPb(Vertex.PointF);
            }
            ((FormParse)ActPoly.Parser).Ptool.UpdateVertexDraw();
			((FormParse)ActPoly.Parser).Ptool.UpdateInactivePaths();

            ((FormParse)ActPoly.Parser).Ptool.MovingPathPt = oldMovingPathPt;   //  complement to the expression above, "bool oldMovingPathPt = ..."
        }
    }
    /// <summary>
    /// Create a link,  replaces Undo/Redo call sign 108
    /// </summary>
    public class Cmd_Link : Command
    {
        public PolygonFlist ToSwallow, ToUnswallow;
        public PolygonF ActivePoly;
        public PathF ActivePath;
        public GlidePath Glider;
        public EdgeGroupArray BustedEdges;
        public Cmd_Link(GlidePath Glider, PathF ActivePath, PolygonFlist ToSwallow, PolygonFlist ToUnswallow, EdgeGroupArray BustedEdges)
        {
            this.ToSwallow = ToSwallow;
            this.ToUnswallow = ToUnswallow;
            this.ActivePath = ActivePath;
            this.Glider = Glider;
            this.BustedEdges = BustedEdges;
        }
        public override void Redo()
        {
            if(ActivePath.GetType()==typeof(PolygonF))
                ActivePoly = (PolygonF)ActivePath;
            else
                ActivePoly = ((ContourF)ActivePath).PolyOwner;
            //	Swallowing requires doubly linking two lists.
            if(ToSwallow != null)
            {
                for(int i=0;i<ToSwallow.Count;i++)
                {
                    ActivePoly.Swallowed.Add(ToSwallow[i]);
                    ToSwallow[i].SwallowedBy = ActivePoly;
                }
            }
            if(ToUnswallow != null)
            {
                for(int i=0;i<ToUnswallow.Count;i++)
                {
                    ActivePoly.Swallowed.Remove(ToUnswallow[i]);
                    ToUnswallow[i].SwallowedBy = null;
                }
            }

            int backInd = ActivePath.IndexOf(Glider[0].Tail);
            int frontInd = ActivePath.IndexOf(Glider.Head);
            int ind = Math.Min(frontInd,backInd);
            if(ActivePath.GetType()==typeof(PolygonF))
                ind = PathTool.InsertBetweenPolyInd(ActivePath.Count,backInd,frontInd);
            int temp;
            for(int i=0;i<Glider.Count;i++)
            {
                ActivePath.Insert(ind,Glider[i].Intersection);
                if(ActivePath.GetType()==typeof(PolygonF))
                {
                    Glider[i].Intersection.Polygons.Add(ActivePath);
                    temp = ind;
                    if(ind == frontInd)
                    {
                        frontInd = ind = PathTool.NextIndInDirection(ActivePath.Count,backInd,frontInd);
                        backInd = temp;
                    }
                }
                else
                {
                    Glider[i].Intersection.Contours.Add(ActivePath);
                    if(backInd > frontInd)
                        ind++;
                }
            }

            //  Null any edge validation
            BustedEdges = null;

            //	Update drawing stuff for polygons only.
            if(ActivePath.GetType()==typeof(PolygonF))
            {
                //	Keep track of busted edges after linking
                BustedEdges = ((PolygonF)ActivePath).ValidateEdges();

                PolygonF changed = Glider.GetChangedRegion();
                Rectangle clipRect = changed.GetBoundingRect(0.0f);
                Bitmap bmp = ((FormParse)ActivePoly.Parser).Ptool.GetBitmap(ActivePath);
                Color col = ((FormParse)ActivePoly.Parser).Ptool.GetColor(ActivePath);
                int left = Math.Max(clipRect.Left,0);
                int top = Math.Max(clipRect.Top,0);
                int right = Math.Min(clipRect.Right,bmp.Width+1);
                int bottom = Math.Min(clipRect.Bottom,bmp.Height+1);
                for(int x = left;x<right;x++)
                {
                    for(int y = top;y<=bottom;y++)
                        if(changed.InPoly(x,y)>0 && !ActivePoly.Swallowed.ContainsPoint(x,y))
                        {
                            ActivePoly.Pix.AddPixel(x,y);
                            bmp.SetPixel(x,y,col);
                        }
                }
            }
            ((FormParse)ActivePoly.Parser).Ptool.UpdateVertexDraw();
			((FormParse)ActivePoly.Parser).Ptool.UpdateInactivePaths();
        }
        public override void Undo()
        {
            if(ActivePath.GetType()==typeof(PolygonF))
                ActivePoly = (PolygonF)ActivePath;
            else
                ActivePoly = ((ContourF)ActivePath).PolyOwner;
            //	Swallowing requires doubly linking two lists.
            if(ToSwallow != null)
            {
                for(int i=0;i<ToSwallow.Count;i++)
                {
                    ActivePoly.Swallowed.Add(ToSwallow[i]);
                    ToSwallow[i].SwallowedBy = ActivePoly;
                }
            }
            if(ToUnswallow != null)
            {
                for(int i=0;i<ToUnswallow.Count;i++)
                {
                    ActivePoly.Swallowed.Remove(ToUnswallow[i]);
                    ToUnswallow[i].SwallowedBy = null;
                }
            }

            for(int i=0;i<Glider.Count;i++)
            {
                if(ActivePath.GetType()==typeof(PolygonF))
                    Glider[i].Intersection.Polygons.Remove(ActivePath);
                else
                    Glider[i].Intersection.Contours.Remove(ActivePath);
                ActivePath.Remove(Glider[i].Intersection);
            }

            //	Restore busted edges only on Undo.
            if(BustedEdges!= null)
                ((FormParse)ActivePoly.Parser).Opd.RestoreEdges(BustedEdges,true);

            //	Update drawing stuff.
            if(ActivePath.GetType()==typeof(PolygonF))
            {
                PolygonF changed = Glider.GetChangedRegion();
                Rectangle clipRect = changed.GetBoundingRect(0.0f);
                Bitmap bmp = ((FormParse)ActivePoly.Parser).Ptool.GetBitmap(ActivePoly);
                if(bmp!=null)
                {
                    int left = Math.Max(clipRect.Left,0);
                    int top = Math.Max(clipRect.Top,0);
                    int right = Math.Min(clipRect.Right,bmp.Width+1);
                    int bottom = Math.Min(clipRect.Bottom,bmp.Height+1);
                    for(int x = left;x<right;x++)
                        for(int y = top;y<=bottom;y++)
                            if(changed.InPoly(x,y)>0)
                            {
                                ActivePoly.Pix.RemovePixel(x,y);
                                bmp.SetPixel(x,y,Color.Transparent);
                            }
                }
            }

            ((FormParse)ActivePoly.Parser).Ptool.UpdateVertexDraw();
			((FormParse)ActivePoly.Parser).Ptool.UpdateInactivePaths();
        }
    }
    /// <summary>
    /// An executable command for moving a node in the TreeView.
    /// </summary>
    public class Cmd_MoveNode : Command
    {
        /// <summary>
        /// The node to be added.
        /// </summary>
        public TreeNode Node;
        /// <summary>
        /// The node to be moved from.
        /// </summary>
        public TreeNode MoveFrom;
        /// <summary>
        /// The node to be moved to.
        /// </summary>
        public TreeNode MoveTo;
        /// <summary>
        /// This member is constructed on a "Redo" and represents the occlusion bridges that may have been rendered invalid.
        /// </summary>
        public OcclusionBridgeList BustedBridges;
        /// <summary>
        /// This member is constructed on a "Redo" and represents the owned edges that may have been rendered invalid.
        /// </summary>
        public EdgeGroupArray BustedEdges;
        /// <summary>
        /// Redo records the thing that was active prior to this operation.
        /// </summary>
        public SelectionRecord PreviousSelection=null;
        /// <summary>
        /// Redo records the thing that was active after to this operation.
        /// </summary>
        public SelectionRecord NextSelection=null;
        /// <summary>
        /// The FormParse windows form (main application).
        /// </summary>
        public FormParse Parser;
        /// <summary>
        /// Constructs a command to move a node in the TreeView.
        /// </summary>
        /// <param name="Node"></param>
        /// <param name="MoveFrom">A TreeNode with a tag that is either a CategoryNode, ParsedObject, PolygonF, or ContourF.</param>
        /// <param name="MoveTo">A TreeNode with a tag that is the same type as MoveFrom but a different instance, and cannot be a descendant or ancestor of MoveFrom.</param>
        public Cmd_MoveNode(FormParse Parser, TreeNode Node,TreeNode MoveFrom,TreeNode MoveTo)
        {
            this.MoveFrom = MoveFrom;
            this.MoveTo = MoveTo;
            this.Parser = Parser;

            this.Node = null;   //  defaults to an invalid command (because when Node is null, Undo and Redo do nothing).

            //  Check for validity before this.Node is set.
            if(MoveFrom==null)
            {
                if(Node.Tag.GetType()==typeof(CategoryNode) && MoveTo.Tag.GetType()==typeof(CategoryNode))
                {
                    this.Node = Node;
                }
                else if(Node.Tag.GetType()==typeof(ParsedObject) && MoveTo.Tag.GetType()==typeof(CategoryNode))
                {
                    this.Node = Node;
                }
                else if(Node.Tag.GetType()==typeof(PolygonF) && MoveTo.Tag.GetType()==typeof(ParsedObject))
                {
                    this.Node = Node;
                }
                else if(Node.Tag.GetType()==typeof(ContourF) && MoveTo.Tag.GetType()==typeof(PolygonF))
                {
                    this.Node = Node;
                }
                else if(Node.Tag.GetType()==typeof(Circle) && MoveTo.Tag.GetType()==typeof(Circle[]))
                {
                    this.Node = Node;
                }
            }
            else if(MoveTo==null)
            {
                if(Node.Tag.GetType()==typeof(CategoryNode) && MoveFrom.Tag.GetType()==typeof(CategoryNode))
                {
                    this.Node = Node;
                }
                else if(Node.Tag.GetType()==typeof(ParsedObject) && MoveFrom.Tag.GetType()==typeof(CategoryNode))
                {
                    this.Node = Node;
                }
                else if(Node.Tag.GetType()==typeof(PolygonF) && MoveFrom.Tag.GetType()==typeof(ParsedObject))
                {
                    this.Node = Node;
                }
                else if(Node.Tag.GetType()==typeof(ContourF) && MoveFrom.Tag.GetType()==typeof(PolygonF))
                {
                    this.Node = Node;
                }
                else if(Node.Tag.GetType()==typeof(Circle) && MoveFrom.Tag.GetType()==typeof(Circle[]))
                {
                    this.Node = Node;
                }
            }
            else
            {
                if(Node.Tag.GetType()==typeof(CategoryNode) && MoveFrom.Tag.GetType()==typeof(CategoryNode) && MoveTo.Tag.GetType()==typeof(CategoryNode))
                {
                    this.Node = Node;
                }
                else if(Node.Tag.GetType()==typeof(ParsedObject) && MoveFrom.Tag.GetType()==typeof(CategoryNode) && MoveTo.Tag.GetType()==typeof(CategoryNode))
                {
                    this.Node = Node;
                }
                else if(Node.Tag.GetType()==typeof(PolygonF) && MoveFrom.Tag.GetType()==typeof(ParsedObject) && MoveTo.Tag.GetType()==typeof(ParsedObject))
                {
                    this.Node = Node;
                }
                else if(Node.Tag.GetType()==typeof(ContourF) && MoveFrom.Tag.GetType()==typeof(PolygonF) && MoveTo.Tag.GetType()==typeof(PolygonF))
                {
                    this.Node = Node;
                }
            }
        }
        public override void Redo()
        {
            if(this.Node!=null) // if this is a valid command
            {
                //  Record the previous selection and clear bitmaps
                PreviousSelection = Parser.CurrentSelection;
                SelectionRecord.ClearBitmaps(PreviousSelection.CnPix,PreviousSelection,Parser);

                //  Put the TreeView on hold.
                Parser.Tv.BeginUpdate();

                //  move the node
                bool last = Parser.Tv_AfterSelect_SuppressSwitchActive;
                Parser.Tv_AfterSelect_SuppressSwitchActive = true;
                if(MoveFrom!=null)
                    MoveFrom.Nodes.Remove(Node);
                if(MoveTo!=null)
                    MoveTo.Nodes.Add(Node);

                //  Record the next active thing
                if(MoveTo!=null)
                    NextSelection = new SelectionRecord(Node.Tag,Parser);
                else
                    NextSelection = new SelectionRecord(Parser.Tv.SelectedNode.Tag,Parser);
                Parser.Tv.SelectedNode = NextSelection.SelNode;

                //  Remove event handling suppression
                Parser.Tv_AfterSelect_SuppressSwitchActive = last;

                //  update the data structure
                UpdateDataStructure(Node,MoveFrom,MoveTo,Parser);
                Parser.Opd.ActiveThing = NextSelection.SelThing;

                //  Update the current selection and paint bitmaps
                Parser.CurrentSelection = NextSelection;
                Parser.PreviousSelection = PreviousSelection;
                SelectionRecord.PaintBitmaps(NextSelection.CnPix,NextSelection,Parser);

                //  Special processing for when MoveFrom!=null --> vertices, bridges, edges
                if(MoveFrom!=null)
                {
                    if(Node.Tag.GetType()==typeof(PolygonF))
                    {
                        //  Validate Bridges and Owned Edges, saving any that may have been destroyed in the process.
                        BustedEdges = ((PolygonF)Node.Tag).ValidateEdges();
                        BustedBridges = ((ParsedObject)MoveFrom.Tag).ValidateBridges();
                        //  Update the current representation of edges.
                        if((BustedBridges!=null && BustedBridges.Count>0) || (BustedEdges!=null && BustedEdges.Count>0))
                            ((PolygonF)Node.Tag).CalcEdges();
                    }
                }

                //  Take the TreeView off of hold
                Parser.Tv.EndUpdate();

                //	Calculate edges.
                if(Parser.CurrentSelection.Poly!=null)
                    Parser.CurrentSelection.Poly.CalcEdges();

                //  Update Vertex Draw
                Parser.Ptool.UpdateVertexDraw();

                //  Update the tool
                Parser.pbTool.Invalidate();
                if(Parser.CurrentSelection.SelThing==null)
                    Parser.CurrentTool = ToolType.None;
                else if(Parser.CurrentSelection.SelThing.GetType()==typeof(PolygonF))
                    Parser.CurrentTool = ToolType.Polygon;
                else if(Parser.CurrentSelection.SelThing.GetType()==typeof(ContourF))
                    Parser.CurrentTool = ToolType.Contour;
                else if(Parser.CurrentSelection.SelThing.GetType()==typeof(Circle))
                    Parser.CurrentTool = ToolType.Circle;
                else
                    Parser.CurrentTool = ToolType.None;

                Parser.Pb.Invalidate();
            }
        }
        public override void Undo()
        {
            if(this.Node!=null) // if this is a valid command
            {
                //  Record the previous selection and clear bitmaps
                SelectionRecord.ClearBitmaps(NextSelection.CnPix,NextSelection,Parser);

                //  Put the TreeView on hold.
                Parser.Tv.BeginUpdate();

                //  move the node
                bool last = Parser.Tv_AfterSelect_SuppressSwitchActive;
                Parser.Tv_AfterSelect_SuppressSwitchActive = true;
                if(MoveTo!=null)
                    MoveTo.Nodes.Remove(Node);
                if(MoveFrom!=null)
                    MoveFrom.Nodes.Add(Node);

                //  Record the next active thing
                if(MoveFrom!=null)
                    PreviousSelection = new SelectionRecord(Node.Tag,Parser);
                else
                    PreviousSelection = new SelectionRecord(Parser.Tv.SelectedNode.Tag,Parser);
                Parser.Tv.SelectedNode = PreviousSelection.SelNode;

                //  Remove event handling suppression
                Parser.Tv_AfterSelect_SuppressSwitchActive = last;

                //  update the data structure
                UpdateDataStructure(Node,MoveTo,MoveFrom,Parser);
                Parser.Opd.ActiveThing = PreviousSelection.SelThing;

                //  Update the current selection and paint bitmaps
                Parser.CurrentSelection = PreviousSelection;
                Parser.PreviousSelection = NextSelection;
                SelectionRecord.PaintBitmaps(PreviousSelection.CnPix,PreviousSelection,Parser);

                //  Special processing for moving a polygon into a different parsed object
                if(MoveFrom!=null)
                {
                    if(Node.Tag.GetType()==typeof(PolygonF))
                    {
                        int i;
                        //  Restore Bridges and Owned Edges that may have been destroyed during Redo.
                        if(BustedBridges!=null)
                        {
                            for(i=0;i<((ParsedObject)MoveFrom.Tag).Bridges.Count;i++)
                                ((ParsedObject)MoveFrom.Tag).Bridges.Add(BustedBridges[i]);
                        }
                        if(BustedEdges!=null)
                        {
                            for(i=0;i<BustedEdges.Count;i++)
                                ((PolygonF)Node.Tag).OwnedEdges.Add(BustedEdges[i].DominantEdge);
                        }
                        //  Update the current representation of edges.
                        if((BustedBridges!=null && BustedBridges.Count>0) || (BustedEdges!=null && BustedEdges.Count>0))
                            ((PolygonF)Node.Tag).CalcEdges();
                    }
                }

                //  Take the TreeView off of hold
                Parser.Tv.EndUpdate();

                //	Calculate edges.
                if(Parser.CurrentSelection.Poly!=null)
                    Parser.CurrentSelection.Poly.CalcEdges();

                //  Update Vertex Draw
                Parser.Ptool.UpdateVertexDraw();

                //  Update the tool
                Parser.pbTool.Invalidate();
                if(Parser.CurrentSelection.SelThing==null)
                    Parser.CurrentTool = ToolType.None;
                else if(Parser.CurrentSelection.SelThing.GetType()==typeof(PolygonF))
                    Parser.CurrentTool = ToolType.Polygon;
                else if(Parser.CurrentSelection.SelThing.GetType()==typeof(ContourF))
                    Parser.CurrentTool = ToolType.Contour;
                else if(Parser.CurrentSelection.SelThing.GetType()==typeof(Circle))
                    Parser.CurrentTool = ToolType.Circle;
                else
                    Parser.CurrentTool = ToolType.None;

                Parser.Pb.Invalidate();
            }
        }
        public static void UpdateDataStructure(TreeNode node,TreeNode moveFrom,TreeNode moveTo,FormParse parser)
        {
            //  update the data structure
            if(node.Tag.GetType()==typeof(CategoryNode))
            {
                if(moveFrom!=null)
                {
                    ((CategoryNode)moveFrom.Tag).Sub.Remove(((CategoryNode)node.Tag));
                    ((CategoryNode)node.Tag).RemoveSwallowedBy(true);
                    ((CategoryNode)node.Tag).RemoveVertexF();
                }
                if(moveTo!=null)
                {
                    //  If not already contained in the superordinate category
                    if (!((CategoryNode)moveTo.Tag).Sub.Contains(((CategoryNode)node.Tag)))
                        // Add to superordinate category
                        ((CategoryNode)moveTo.Tag).Sub.Add(((CategoryNode)node.Tag));
                    ((CategoryNode)node.Tag).Super = (CategoryNode)moveTo.Tag;
                    ((CategoryNode)node.Tag).RestoreSwallowedBy(true);
                    ((CategoryNode)node.Tag).ReclaimVertices();
                }
            }
            else if(node.Tag.GetType()==typeof(ParsedObject))
            {
                if(moveFrom!=null)
                {
                    ((CategoryNode)moveFrom.Tag).Objects.Remove(((ParsedObject)node.Tag));
                    ((ParsedObject)node.Tag).RemoveVertexF();
                    ((ParsedObject)node.Tag).RemoveSwallowedBy();
                }
                if(moveTo!=null)
                {
                    ((CategoryNode)moveTo.Tag).Objects.Add(((ParsedObject)node.Tag));
                    ((ParsedObject)node.Tag).Category = (CategoryNode)moveTo.Tag;
                    ((ParsedObject)node.Tag).ReclaimVertices();
                    ((ParsedObject)node.Tag).RestoreSwallowedBy();
                }
            }
            else if(node.Tag.GetType()==typeof(PolygonF))
            {
                if(moveFrom!=null)
                {
                    ((ParsedObject)moveFrom.Tag).Polygons.Remove(((PolygonF)node.Tag));
                    ((PolygonF)node.Tag).RemoveSwallowedBy();
                    ((PolygonF)node.Tag).RemoveVertexF();
                }
                if(moveTo!=null)
                {
                    ((ParsedObject)moveTo.Tag).Polygons.Add(((PolygonF)node.Tag));
                    ((PolygonF)node.Tag).Owner = (ParsedObject)moveTo.Tag;
                    ((PolygonF)node.Tag).RestoreSwallowedBy();
                    ((PolygonF)node.Tag).ReclaimVertices();
                }
            }
            else if(node.Tag.GetType()==typeof(ContourF))
            {
                if(moveFrom!=null)
                {
                    ((PolygonF)moveFrom.Tag).Contours.Remove(((ContourF)node.Tag));
                    ((ContourF)node.Tag).RemoveVertexF();
                }
                if(moveTo!=null)
                {
                    ((PolygonF)moveTo.Tag).Contours.Add(((ContourF)node.Tag));
                    ((ContourF)node.Tag).Owner = ((PolygonF)moveTo.Tag).Owner;
                    ((ContourF)node.Tag).PolyOwner = (PolygonF)moveTo.Tag;
                    ((ContourF)node.Tag).ReclaimVertices();
                }
            }
            else if(node.Tag.GetType()==typeof(Circle))
            {
                if(moveFrom!=null)
                {
                    parser.Opd.RemoveCircle((Circle)node.Tag);
                    moveFrom.Tag = parser.Opd.Circles;
                }
                if(moveTo!=null)
                {
                    parser.Opd.AddCircle((Circle)node.Tag);
                    moveTo.Tag = parser.Opd.Circles;
                }
            }
        }
    }
    /// <summary>
    /// An executable command for adding a node to the TreeView.
    /// </summary>
    public class Cmd_AddNode : Command
    {
        protected Cmd_MoveNode InnerCmd = null;
        public TreeNode AddTo
        {
            get
            {
                if(InnerCmd==null)
                    return null;
                return InnerCmd.MoveTo;
            }
        }
        public TreeNode Node
        {
            get
            {
                if(InnerCmd==null)
                    return null;
                return InnerCmd.Node;
            }
        }
        public Cmd_AddNode(FormParse Parser, TreeNode Node,TreeNode AddTo)
        {
            InnerCmd = new Cmd_MoveNode(Parser, Node,null,AddTo);
        }
        public override void Redo()
        {
            InnerCmd.Redo();
        }
        public override void Undo()
        {
            InnerCmd.Undo();
        }
    }
    /// <summary>
    /// An executable command for deleting a node from the TreeView.
    /// </summary>
    public class Cmd_DeleteNode : Command
    {
        protected Cmd_MoveNode InnerCmd = null;
        public TreeNode DeleteFrom
        {
            get
            {
                if(InnerCmd==null)
                    return null;
                return InnerCmd.MoveFrom;
            }
        }
        public TreeNode Node
        {
            get
            {
                if(InnerCmd==null)
                    return null;
                return InnerCmd.Node;
            }
        }
        public Cmd_DeleteNode(FormParse Parser, TreeNode Node,TreeNode DeleteFrom)
        {
            InnerCmd = new Cmd_MoveNode(Parser,Node,DeleteFrom,null);
        }
        public override void Redo()
        {
            InnerCmd.Redo();
        }
        public override void Undo()
        {
            InnerCmd.Undo();
        }
    }
    /// <summary>
    /// An executable command for switching the active TreeNode.
    /// </summary>
    public class Cmd_SwitchActive : Command
    {
        public FormParse Parser;
        public object SwitchFrom;
        public object SwitchTo;
        public Cmd_SwitchActive(FormParse Parser,object SwitchFrom,object SwitchTo)
        {
            this.Parser = Parser;
            this.SwitchFrom = SwitchFrom;
            this.SwitchTo = SwitchTo;
        }
        public override void Redo()
        {
            Parser.ActivateSelection(SwitchTo,false);
        }
        public override void Undo()
        {
            Parser.ActivateSelection(SwitchFrom,false);
        }
    }
}